{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "28e6c32d",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "from torchsummary import summary\n",
    "from torchvision import transforms\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision.datasets import MNIST\n",
    "from torch.utils.tensorboard import SummaryWriter\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ec1b585",
   "metadata": {},
   "source": [
    "# 定义计算设备和超参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b161acff",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 定义计算设备\n",
    "device=torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d127345a",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 定义超参数\n",
    "learning_rate=0.001\n",
    "num_classes=10\n",
    "batch_size=64\n",
    "num_epochs=20"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ea746b1",
   "metadata": {},
   "source": [
    "# 准备MNIST数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a5746523",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 注意root参数表示存在MNIST/processed/training.pt和MNIST/processed/test.pt的子目录的根目录\n",
    "# 训练数据集\n",
    "train_dataset=MNIST(root=\"/dataset/train/\",\n",
    "                   train=True,\n",
    "                   transform=transforms.Compose([\n",
    "                       transforms.Resize(size=(28,28)),\n",
    "                       transforms.ToTensor()\n",
    "                   ]))\n",
    "# 测试数据集\n",
    "test_dataset=MNIST(root=\"/dataset/train\",\n",
    "                   train=False,\n",
    "                   transform=transforms.Compose([\n",
    "                       transforms.Resize(size=(28,28)),\n",
    "                       transforms.ToTensor()\n",
    "                   ]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "945ff8a4",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The size of train/test dataset is 60000/10000.\n"
     ]
    }
   ],
   "source": [
    "# 数据探索\n",
    "train_dataset_size = len(train_dataset)\n",
    "test_dataset_size = len(test_dataset)\n",
    "print(\"The size of train/test dataset is {}/{}.\".format(train_dataset_size,test_dataset_size))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9fd9c2f1",
   "metadata": {},
   "source": [
    "# 定义DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "77c63044",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 定义数据集加载器\n",
    "train_loader=DataLoader(dataset=train_dataset,\n",
    "                        batch_size=batch_size,\n",
    "                        shuffle=True)\n",
    "test_loader=DataLoader(dataset=test_dataset,\n",
    "                        batch_size=batch_size,\n",
    "                        shuffle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "381e2702",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "938 157\n"
     ]
    }
   ],
   "source": [
    "print(len(train_loader),len(test_loader))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "2d0a84cb",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "937 156\n"
     ]
    }
   ],
   "source": [
    "print(len(train_dataset)//batch_size,len(test_dataset)//batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "0595cbea",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[tensor([[[[0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           ...,\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.]]],\n",
       " \n",
       " \n",
       "         [[[0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           ...,\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.]]],\n",
       " \n",
       " \n",
       "         [[[0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           ...,\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.]]],\n",
       " \n",
       " \n",
       "         ...,\n",
       " \n",
       " \n",
       "         [[[0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           ...,\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.]]],\n",
       " \n",
       " \n",
       "         [[[0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           ...,\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.]]],\n",
       " \n",
       " \n",
       "         [[[0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           ...,\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.],\n",
       "           [0., 0., 0.,  ..., 0., 0., 0.]]]]),\n",
       " tensor([8, 3, 5, 9, 5, 6, 9, 4, 3, 9, 6, 5, 8, 6, 6, 3, 7, 0, 3, 9, 0, 5, 0, 0,\n",
       "         3, 0, 6, 4, 9, 7, 8, 3, 3, 4, 1, 7, 7, 3, 9, 5, 4, 7, 7, 0, 1, 0, 5, 5,\n",
       "         9, 1, 0, 4, 3, 5, 0, 9, 4, 8, 1, 3, 3, 6, 1, 1])]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看第一次采样结果\n",
    "train_loader.__iter__().next()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2465036b",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "images,labels=next(iter(train_loader))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "a4ae91bb",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([64, 1, 28, 28])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "images.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "6016c411",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([64])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "labels.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f33027f",
   "metadata": {},
   "source": [
    "# 显示部分手写体数字图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "7d41a5ba",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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JriCNwAAQAKaJQAAgAQ0SwAAAAlolgAAABLQLAEAACSgWQIAAEhAswQAAJCAZgkAACABzRIAAEACmiUAAIAENEsAAAAJaJYAAAAS0CwBAAAkoFkCAABIsHGtJ4DC7bfffsH80UcfLXgZ11xzTTC/8MILg/mKFSsKXjYAAGnEniUAAIAENEsAAAAJaJYAAAAS0CwBAAAkKOkAbzObJ2mlpHWS1rr7wHJMCmhU1ASQj5pAGpTjbLiD3H1pGZaDNpxwwgnBfNmyZZHs/vvvD479zne+E8x33nnnYD5s2LBI9sknn8RNERnURJltvHH4r6oxY8YE89122y2Sde3aNTj23XffDeZxNbHVVltFsm9/+9vBsfPnzw/mTYiaKMHQoUMjWb9+/YpaxnXXXRfJVq1a1e45NRs+hgMAAEhQarPkkv5mZjPNbGxogJmNNbNWM2stcV1AI6AmgHzUBBpeqR/D7e/ub5vZdpIeMLPZ7v5I7gB3b5HUIklm5iWuD6h31ASQj5pAwytpz5K7v539ukTS7ZIGlWNSQKOiJoB81ATSoN17lsxsC0kbufvK7PNDJP1X2WaGiNNPPz2YH3LIIZHs73//e3DsF77whWB+8MEHB/Pbb789ko0fPz449uWXXw7mzYKaKI+99torkv3ud78Ljh04sPQTqxYvXhzM16xZE8x79OgRyR544IHg2K997WvBPO6g8rShJsJ69eoVzI8//vhg/uMf/ziSfeYznwmONbNgPnv27Eh27733xk2xZHHb+NFHHwXz9957r2JzKYdSPobrJun27B/MxpJucvf7yjIroDFRE0A+agKp0O5myd3nStqjjHMBGho1AeSjJpAWXDoAAAAgAc0SAABAApolAACABOW43QnKLO42C0899VQwf/DBBwte9pFHHhnMH3300WB+6KGHRrLnnnsuOPYHP/hBwfMAtt1222B+2223RbLPf/7zwbFxZ5WFljFt2rTg2CeeeCKYf/rpp8H8gw8+iGRxNVvsvNEc4s56+/nPf16xdZ500kmRLO5suLj38ymnnBLMv/71r0eyLl26BMeuXr06mI8bNy6SFfNvW6WxZwkAACABzRIAAEACmiUAAIAENEsAAAAJaJYAAAAScDZcA4m799q6desKXsY777wTzK+77rpgfumllxa8bKAYZ511VjAPnUEWdwbN1KlTg/kPf/jDSBY6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hZoA8lETSLuirrNkZn0k7SnpKUnd3P3ft1lepMzu19DPjJUUvqAK0OCoCSAfNYE0KvgAbzPbUtJUSePcfUXu9zxzUE/0wJ7M91rcfaC7DyxppkCdoSaAfNQE0qqgZsnMNlGmAG5092nZeLGZdc9+v7ukJZWZIlB/qAkgHzWBNCvkbDiTdIOkV9x9Ys637pI0StJl2a93VmSGVXTCCScUPHaHHXao4EzCJk6cGMxnzpwZye67775KT6dpNVNNxJk/f34k+/TTT2swk7DQGaI//elPi1rGW2+9Fcyb/cy3kEapibgzim+99dZIduihhwbHnnjiiQWvL+4su9AZ1kl5MeLOeps+fXoku+KKK4Jj//WvfwXzjz/+uP0Ta3CFHLO0n6QTJb1oZs9lswuUefPfZmajJb0paXhFZgjUH2oCyEdNINXabJbc/TFJcRehGFze6QD1j5oA8lETSDuu4A0AAJCAZgkAACABzRIAAEACK8fR9wWvzKx6K2uH4cPDxx7ecMMNkaxjx47BsR988EFZ55TrM5/5TDD/yle+Eslmz55dsXk0sJn1dh2Xeq+JYsTdT2rAgAHBfMKECcH8+eefL3id/fr1C+bnnntuJPva174WHBv3d2BoGZJ09dVXFzi7hkBNSDrooIMi2bRp0wIjpU6dOhW83GLPhovz2muvRbL33nsvOPa8884L5s8++2wk4x6iQcGaYM8SAABAApolAACABDRLAAAACWiWAAAAEnCAdwGOOeaYSHbYYYcVPFaStt5665LnscceewTzl156qeRlNwkOZq2g3r17B/O4A7933XXXSk4nIu42ENdff30w/853vlPJ6dQLaiJG6KBvSRo3blww33333SNZ3MHWxfrnP/8ZyRYsWFCWZSOCA7wBAACKRbMEAACQgGYJAAAgAc0SAABAApolAACABJwNh2bCmT81sM022wTzU045JZiffPLJkaxLly7BsVOnTg3mzz33XCSbN29ecOyMGTOCeZOgJoB8nA0HAABQLJolAACABDRLAAAACWiWAAAAEtAsAQAAJHH3xIekXpIelPSypFmSvpfNL5b0tqTnso/DC1iW8+BRw0drW+/RQh6iJnik50FN8OCR/wjWxMZq21pJ4939WTPrJGmmmT2Q/d5V7v7LApYBpAk1AeSjJpBqbTZL7r5Q0sLs85Vm9oqknpWeGFCvqAkg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fW+88Uatp4Aa2mKLLYL5ZZddFsxDZ77dd999wbHnnHNOMK/kGaKh2/eYWXDsoYceGswfffTRYL7//vtHsocffjg4Nu5sODSuF154oeCxRx55ZDCv9/fFmjVraj2FusOeJQAAgAQ0SwAAAAlolgAAABLQLAEAACRos1kys0lmtsTMXsrJLjazt83suezj8MpOE6gf1ASQj5pA2hVyNtxkSf8tacMbp13l7r8s+4yA+jdZKauJ7t27B/PQ/a6k8P3eFi1aFBzbuXPnYL733nsXNjlJffr0Ceb77rtvMD/ppJMiWdw96orZRkk6++yzI1ncPReb6B5wk5WymogTd9+0X/ziF5Es7p6btTgbLlQTKFybe5bc/RFJy6owF6AhUBNAPmoCaVfKMUtnmtkL2d2vXeIGmdlYM2s1M249j7SjJoB81ARSob3N0rWSdpQ0QNJCSVfGDXT3Fncf6O4D27kuoBFQE0A+agKp0a5myd0Xu/s6d18v6TpJg8o7LaCxUBNAPmoCadKu252YWXd3X5h9ebSkl5LGozl85StfCeatrenfs97oNfH+++8H89mzZwfzXXbZJZKdfPLJwbGjRo1q97zaEncLk9DB2atXrw6OnTBhQjCfPHlyMP/www8Lm1yTa/SaiLNixYpgHqqVo446Kjh23Lhxwfz1118P5gceeGAki7uVSteuXYN5ly6xn4JGjB49OpjH/X1QzC1gGlWbzZKZ3SzpQEnbmNkCSRdJOtDMBkhySfMknVa5KQL1hZoA8lETSLs2myV3HxGIb6jAXICGQE0A+agJpB1X8AYAAEhAswQAAJCAZgkAACCBxV3SvyIrM6veylCU3r17B/N58+YVvIypU6cG8+OOO649U6qEmfV2HZd6r4m498X06dMjWd++fYNj4/6OiTs7LXTGTdx764knngjm999/fyRbvHhxcGzozL4mQk2USeh2J+eee25wbNxZnMVYs2ZNMI+rq44dO0ayTTbZJDj2+OOPD+a33XZbgbNraMGaYM8SAABAApolAACABDRLAAAACWiWAAAAEtAsAQAAJGjXveEa3bbbbhvM485QWLJkSSWnkxpx90xC43rrrbeC+R577BHJevToUdSy33nnnXbNqRBr166NZA8++GDF1gecd955keyf//xncOy3vvWtYN65c+dgftNNN0WyWbNmBcc+99xzBS8j7qw3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\n",
      "text/plain": [
       "<Figure size 720x720 with 9 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 显示9张手写数字图片\n",
    "plt.figure(figsize=(10,10))\n",
    "index=0\n",
    "for img,target in zip(images[:9],labels[:9]):\n",
    "    index+=1\n",
    "    plt.subplot(3,3,index)\n",
    "    plt.title(\"label:{}\".format(target))\n",
    "    plt.imshow(img.permute(1,2,0),cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "d531e088",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "784"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "images[0].numel()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce5030c6",
   "metadata": {},
   "source": [
    "# 将数据集参数提取为C/C++头文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "fc4280c0",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 将手写体图像转换为一维数组存储在.h文件中\n",
    "for im,lab in zip(images,labels):\n",
    "    length=im.numel()\n",
    "    im=str(im.tolist())\n",
    "    im=im.replace(\"[\",\"\")\n",
    "    im=im.replace(\"]\",\"\")\n",
    "    with open(\"input_{}.h\".format(lab),\"w\") as fw:\n",
    "        fw.write(\"double input_{}[{}] = {{{}}};\\n\".format(lab,length,im))# 三个嵌套大括号表示输出一个大括号"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2b00234",
   "metadata": {},
   "source": [
    "# 定义两层的神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a1751fd1",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "class TwoLayer(nn.Module):\n",
    "    def __init__(self,num_classes):\n",
    "        super(TwoLayer,self).__init__()\n",
    "        self.num_classes=num_classes\n",
    "        self.fc1=nn.Linear(784,64)\n",
    "        self.fc2=nn.Linear(64,num_classes)\n",
    "        self.elu=nn.ELU()\n",
    "    def forward(self,x):\n",
    "        x=x.view(x.size(0),-1)\n",
    "        x=self.fc1(x)\n",
    "        x=self.elu(x)\n",
    "        x=self.fc2(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "0e3814b4",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "model=TwoLayer(num_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "557dbb09",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "model=model.to(device)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48617b2f",
   "metadata": {},
   "source": [
    "# 查看模型结构和参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "1a70f168",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TwoLayer(\n",
       "  (fc1): Linear(in_features=784, out_features=64, bias=True)\n",
       "  (fc2): Linear(in_features=64, out_features=10, bias=True)\n",
       "  (elu): ELU(alpha=1.0)\n",
       ")"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "daac4605",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------------------------------\n",
      "        Layer (type)               Output Shape         Param #\n",
      "================================================================\n",
      "            Linear-1                   [-1, 64]          50,240\n",
      "               ELU-2                   [-1, 64]               0\n",
      "            Linear-3                   [-1, 10]             650\n",
      "================================================================\n",
      "Total params: 50,890\n",
      "Trainable params: 50,890\n",
      "Non-trainable params: 0\n",
      "----------------------------------------------------------------\n",
      "Input size (MB): 0.00\n",
      "Forward/backward pass size (MB): 0.00\n",
      "Params size (MB): 0.19\n",
      "Estimated Total Size (MB): 0.20\n",
      "----------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "summary(model,input_size=(1,28,28))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0eb4dce",
   "metadata": {},
   "source": [
    "# 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "ef83ad90",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 定义损失函数\n",
    "loss_function=nn.CrossEntropyLoss()\n",
    "# 定义优化器\n",
    "optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "091276da",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 事件写入对象\n",
    "writer = SummaryWriter(log_dir=\"model_logs\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "3e643c60",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第1轮训练开始....\n",
      "模型保存成功！\n",
      "第2轮训练开始....\n",
      "模型保存成功！\n",
      "第3轮训练开始....\n",
      "模型保存成功！\n",
      "第4轮训练开始....\n",
      "模型保存成功！\n",
      "第5轮训练开始....\n",
      "模型保存成功！\n",
      "第6轮训练开始....\n",
      "模型保存成功！\n",
      "第7轮训练开始....\n",
      "模型保存成功！\n",
      "第8轮训练开始....\n",
      "模型保存成功！\n",
      "第9轮训练开始....\n",
      "模型保存成功！\n",
      "第10轮训练开始....\n",
      "模型保存成功！\n",
      "第11轮训练开始....\n",
      "模型保存成功！\n",
      "第12轮训练开始....\n",
      "模型保存成功！\n",
      "第13轮训练开始....\n",
      "模型保存成功！\n",
      "第14轮训练开始....\n",
      "模型保存成功！\n",
      "第15轮训练开始....\n",
      "模型保存成功！\n",
      "第16轮训练开始....\n",
      "模型保存成功！\n",
      "第17轮训练开始....\n",
      "模型保存成功！\n",
      "第18轮训练开始....\n",
      "模型保存成功！\n",
      "第19轮训练开始....\n",
      "模型保存成功！\n",
      "第20轮训练开始....\n",
      "模型保存成功！\n"
     ]
    }
   ],
   "source": [
    "# 开始训练\n",
    "for epoch in range(num_epochs):\n",
    "    print(\"第{}轮训练开始....\".format(epoch + 1))\n",
    "    total_train_acc = 0\n",
    "    total_test_acc = 0\n",
    "    # 训练步骤开始\n",
    "    model.train()\n",
    "    for imgs,targets in train_loader:\n",
    "        # 将训练数据样本及其标签迁移到运算设备\n",
    "        imgs=imgs.to(device)\n",
    "        targets=targets.to(device)\n",
    "        # 将神经网络全部参数梯度清零\n",
    "        optimizer.zero_grad()\n",
    "        # 前向传播\n",
    "        outputs=model(imgs)\n",
    "        total_train_acc += (outputs.argmax(dim=1) == targets).sum().item()\n",
    "        loss=loss_function(outputs,targets)\n",
    "        # 反向传播\n",
    "        loss.backward()\n",
    "        # 更新梯度\n",
    "        optimizer.step()\n",
    "    # 测试步骤开始\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        for imgs,targets in test_loader:\n",
    "            # 将测试数据样本及其标签迁移到运算设备\n",
    "            imgs=imgs.to(device)\n",
    "            targets=targets.to(device)  \n",
    "            outputs=model(imgs)\n",
    "            total_test_acc += (outputs.argmax(dim=1) == targets).sum().item()\n",
    "    writer.add_scalars(\"ACC\",{\"train acc\":total_train_acc/train_dataset_size,\n",
    "                              \"test acc\":total_test_acc / test_dataset_size},epoch+1)\n",
    "    torch.save(model.state_dict(), \"models/model_epoch{}.pth\".format(epoch + 1))\n",
    "    print(\"模型保存成功！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "d40f2b41",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "      <iframe id=\"tensorboard-frame-5e271b560a4d12d4\" width=\"100%\" height=\"800\" frameborder=\"0\">\n",
       "      </iframe>\n",
       "      <script>\n",
       "        (function() {\n",
       "          const frame = document.getElementById(\"tensorboard-frame-5e271b560a4d12d4\");\n",
       "          const url = new URL(\"/\", window.location);\n",
       "          const port = 8888;\n",
       "          if (port) {\n",
       "            url.port = port;\n",
       "          }\n",
       "          frame.src = url;\n",
       "        })();\n",
       "      </script>\n",
       "    "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Load the TensorBoard notebook extension\n",
    "%reload_ext tensorboard\n",
    "logs_dir = 'model_logs/'\n",
    "%tensorboard --logdir logs_dir"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "cf17862c",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 关闭事件写入对象\n",
    "writer.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "622bb103",
   "metadata": {},
   "source": [
    "# 模型验证"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "388f89f1",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 创建一个神经网络实例并迁移到device上面\n",
    "best_model=TwoLayer(num_classes)\n",
    "best_model=best_model.to(device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f84fb8d5",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将第15轮训练好的参数加载到神经网络中\n",
    "best_model.load_state_dict(torch.load(\"models/model_epoch15.pth\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "0734b6dd",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TwoLayer(\n",
       "  (fc1): Linear(in_features=784, out_features=64, bias=True)\n",
       "  (fc2): Linear(in_features=64, out_features=10, bias=True)\n",
       "  (elu): ELU(alpha=1.0)\n",
       ")"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看神经网络结构\n",
    "best_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "4061d469",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('fc1.weight',\n",
       "              tensor([[ 0.0178,  0.0287, -0.0187,  ...,  0.0194, -0.0052, -0.0057],\n",
       "                      [-0.0235, -0.0194,  0.0319,  ...,  0.0261,  0.0152, -0.0233],\n",
       "                      [-0.0305,  0.0227, -0.0342,  ...,  0.0221, -0.0182,  0.0337],\n",
       "                      ...,\n",
       "                      [ 0.0119,  0.0035, -0.0068,  ...,  0.0071,  0.0264,  0.0188],\n",
       "                      [ 0.0082, -0.0258, -0.0171,  ...,  0.0027,  0.0207, -0.0112],\n",
       "                      [-0.0096,  0.0233,  0.0208,  ...,  0.0128,  0.0138, -0.0294]],\n",
       "                     device='cuda:0')),\n",
       "             ('fc1.bias',\n",
       "              tensor([ 0.0645,  0.0657, -0.4509,  0.0308, -0.1290, -0.1913,  0.1621,  0.2732,\n",
       "                       0.0875, -0.3909,  0.0919, -0.1450, -0.1341,  0.0322, -0.1189, -0.0927,\n",
       "                      -0.1239,  0.1298,  0.3445,  0.1973, -0.2774, -0.1639,  0.3863, -0.0664,\n",
       "                       0.2876,  0.2047, -0.3421,  0.4779, -0.0565,  0.0490,  0.3046, -0.2873,\n",
       "                      -0.2447, -0.1577,  0.1927,  0.2070, -0.1906,  0.1341,  0.3666, -0.2829,\n",
       "                       0.1180,  0.1624, -0.1195,  0.2131,  0.0904, -0.2094, -0.1996, -0.2258,\n",
       "                      -0.2048,  0.0067, -0.1847, -0.0856,  0.0027, -0.1801,  0.2716, -0.0302,\n",
       "                       0.0830,  0.0073, -0.1085,  0.1555,  0.0806, -0.0302,  0.2447, -0.0890],\n",
       "                     device='cuda:0')),\n",
       "             ('fc2.weight',\n",
       "              tensor([[-3.2873e-01,  1.4542e-01,  1.9585e-01,  9.3548e-02, -3.9231e-01,\n",
       "                        1.7116e-01, -7.6063e-02, -6.6612e-01, -1.3418e-01,  2.3634e-01,\n",
       "                       -6.2801e-01,  2.5581e-01,  1.8382e-01, -4.7878e-01, -1.5281e-01,\n",
       "                       -5.0176e-01,  1.2565e-01, -2.7710e-01,  2.5513e-01,  5.3579e-01,\n",
       "                        1.4911e-01,  5.1501e-01,  9.0224e-02,  1.8102e-02, -1.2033e+00,\n",
       "                       -1.1005e+00,  3.0478e-01, -2.8709e-01, -5.0885e-02,  3.1815e-01,\n",
       "                        5.2992e-01,  2.9806e-01, -1.1646e+00,  1.4703e-01, -1.4537e-01,\n",
       "                        2.7605e-01,  2.5303e-01,  6.3442e-02, -1.7377e-01,  3.7126e-01,\n",
       "                       -6.2891e-01, -3.4709e-01,  1.7149e-01, -5.2413e-01,  2.9267e-03,\n",
       "                       -1.5034e-01, -2.8397e-02,  4.0937e-01,  4.2755e-01,  9.9359e-02,\n",
       "                       -3.2787e-02, -7.2241e-03, -2.7151e-01, -3.3350e-01, -2.2389e-02,\n",
       "                        6.8319e-02, -1.0148e-01, -6.1237e-01,  9.4524e-02,  9.3593e-02,\n",
       "                       -6.6560e-03, -2.5491e-01, -3.6687e-01, -4.4747e-01],\n",
       "                      [ 1.7764e-01,  2.9498e-01, -6.4287e-01, -1.4300e-01,  4.3904e-01,\n",
       "                       -6.0318e-01, -1.5649e-01,  7.9419e-01,  1.5559e-01, -7.2450e-01,\n",
       "                       -3.4049e-01, -5.0671e-01,  1.8624e-01, -2.7707e-01, -1.6296e-01,\n",
       "                       -2.9799e-01, -2.6495e-03,  1.5730e-01, -3.7608e-01,  2.7349e-01,\n",
       "                       -1.6721e-02,  1.6440e-01,  4.5187e-01, -1.0053e-01,  2.1370e-01,\n",
       "                        7.3691e-02,  4.3443e-02,  3.1317e-01, -5.1085e-01,  1.0571e-01,\n",
       "                        6.4636e-01, -2.3957e-01,  7.1024e-02, -2.4789e-02,  7.7823e-01,\n",
       "                       -2.6178e-01,  2.4744e-01, -1.8072e-01,  8.2166e-02, -6.8748e-01,\n",
       "                        5.1931e-02, -6.3001e-01,  1.1073e-01,  7.6199e-01, -8.1195e-01,\n",
       "                       -1.0422e-01,  1.1270e-01,  5.2501e-01, -6.3074e-01,  2.0054e-01,\n",
       "                       -9.9085e-01, -6.8180e-02,  2.4452e-01,  5.4671e-01,  2.9258e-01,\n",
       "                       -1.7784e-01,  4.7128e-01,  1.4141e-01, -5.3936e-01, -1.2501e-01,\n",
       "                       -4.9540e-01,  1.2959e-01,  2.6926e-01,  1.7753e-01],\n",
       "                      [ 3.3615e-01,  3.6794e-01, -2.1197e-01,  4.8494e-01,  5.3516e-01,\n",
       "                       -7.1269e-02, -3.2215e-01, -1.8745e-01, -2.1492e-01, -2.2151e-01,\n",
       "                       -4.5888e-01,  2.1627e-01,  1.2476e-01,  2.8866e-01,  4.3240e-02,\n",
       "                        1.6829e-01, -1.4587e-01,  4.9133e-01, -6.3486e-02,  3.0711e-01,\n",
       "                       -1.3261e+00,  2.5911e-01,  2.0539e-01, -7.2563e-01, -8.8864e-02,\n",
       "                        8.0941e-02,  3.6763e-01,  3.6715e-01,  8.2529e-02, -3.5599e-02,\n",
       "                       -7.6445e-02, -2.7067e-01, -2.0776e-01,  5.6177e-02,  3.8221e-01,\n",
       "                       -6.4885e-01,  3.6053e-01,  2.0457e-01,  4.0774e-01,  4.2271e-02,\n",
       "                       -2.4355e-01, -2.2784e-01,  2.0457e-01,  4.0953e-01,  5.0451e-01,\n",
       "                        5.7754e-02, -1.9610e-01,  3.6673e-01,  3.9790e-01,  1.5432e-01,\n",
       "                        6.3120e-03, -3.5280e-02, -6.5674e-01, -3.1550e-01,  8.5293e-02,\n",
       "                       -1.1078e-01,  3.0834e-01,  6.7486e-01,  3.4464e-01, -8.0002e-01,\n",
       "                       -1.1592e-01,  3.1752e-01, -9.3861e-01,  1.7884e-01],\n",
       "                      [-2.4462e-01,  5.5316e-02, -3.4174e-02,  3.6137e-01,  3.8901e-01,\n",
       "                        8.7821e-02, -8.1419e-01, -6.2757e-01, -6.5992e-01,  2.3877e-01,\n",
       "                        2.2676e-01,  5.4529e-02,  5.3075e-01,  3.3710e-01,  1.7676e-01,\n",
       "                        8.5473e-02, -2.7979e-01,  2.9282e-01, -5.8882e-02, -2.5838e-01,\n",
       "                        2.7391e-01, -2.3492e-01, -8.1990e-02,  2.5677e-01,  3.8174e-01,\n",
       "                        1.3859e-01, -5.4018e-02, -2.4115e-01, -6.7491e-01, -9.4739e-01,\n",
       "                        4.0431e-02,  3.8176e-02,  1.6286e-01,  1.8363e-01, -6.1726e-01,\n",
       "                       -1.7778e-01, -7.6322e-01, -2.2737e-01, -7.3415e-01, -2.9451e-01,\n",
       "                        2.1882e-01,  6.3571e-01,  2.2056e-01,  1.0553e-01, -6.1322e-03,\n",
       "                        4.0994e-02,  2.6272e-01,  1.0448e-01,  1.3366e-01, -5.2152e-01,\n",
       "                       -1.2978e-01, -7.1226e-02, -1.6135e-01,  5.1358e-01,  5.7765e-01,\n",
       "                        4.1851e-01, -3.7172e-02, -2.4685e-01,  2.0610e-01,  5.8088e-01,\n",
       "                       -9.6452e-04,  3.5968e-01, -5.8980e-01, -1.8016e-02],\n",
       "                      [-3.1988e-02,  2.5065e-01,  1.9388e-01,  3.3234e-02, -4.8334e-01,\n",
       "                       -8.4620e-01, -2.8849e-01,  1.6163e-01, -2.5274e-01, -5.3550e-02,\n",
       "                       -7.5537e-03,  5.4875e-01, -9.7539e-01,  1.3418e-01, -1.7338e-01,\n",
       "                       -1.7197e-01,  2.3014e-01, -5.8697e-01,  6.1253e-02, -8.6525e-01,\n",
       "                        1.9866e-01, -1.3059e-01, -1.0132e+00, -6.3428e-01,  2.5966e-01,\n",
       "                        1.4224e-01,  8.1598e-03, -6.4173e-01,  2.7759e-01,  1.6412e-01,\n",
       "                       -6.9785e-01, -1.7593e-01, -3.7645e-02, -7.4107e-01, -1.1719e-01,\n",
       "                        4.9849e-01, -1.8338e-01,  6.2888e-01,  1.2100e-01,  6.2373e-02,\n",
       "                        1.4240e-01, -1.5973e-01, -1.0597e+00,  6.7789e-01, -7.7971e-03,\n",
       "                        1.3605e-01, -4.8337e-01, -4.4347e-01, -3.6032e-01,  2.9446e-01,\n",
       "                       -1.1107e-01, -9.5078e-01,  4.4119e-01,  7.3438e-01, -1.0500e+00,\n",
       "                       -4.1371e-01, -5.0187e-01, -3.4507e-01, -9.1532e-01,  3.7887e-01,\n",
       "                       -8.6554e-02, -3.1299e-01,  3.8587e-01, -2.5540e-02],\n",
       "                      [ 1.1510e-01, -1.1706e+00, -3.0007e-01, -1.4130e-01, -3.0653e-01,\n",
       "                        3.4085e-02,  2.3132e-01,  2.1859e-02,  3.8778e-01, -1.6587e-01,\n",
       "                        2.8636e-01, -9.8550e-02, -5.4274e-01, -1.1604e-01,  4.9620e-02,\n",
       "                       -1.8160e-01, -4.3956e-01,  2.8275e-01,  3.9092e-01,  2.3948e-01,\n",
       "                        8.2579e-02, -9.8299e-01,  5.3807e-01,  4.7456e-01,  2.2002e-01,\n",
       "                        3.2411e-01, -1.1479e+00,  5.0005e-01,  3.6470e-01, -3.7647e-01,\n",
       "                        2.7254e-01,  4.5390e-02,  3.6718e-01, -8.2721e-03, -6.5723e-01,\n",
       "                        1.6777e-01, -4.8730e-01,  3.0590e-01, -9.9856e-02,  1.0530e-01,\n",
       "                        5.1645e-02,  5.1503e-01,  1.8387e-01, -2.4798e-01,  4.0302e-03,\n",
       "                       -2.5445e-01, -2.7967e-01, -7.8326e-01, -8.4945e-02,  3.2456e-01,\n",
       "                        5.1219e-02,  1.8223e-01, -4.0893e-01, -8.5068e-01, -2.0389e-01,\n",
       "                        3.1618e-01,  8.1480e-02, -1.5317e-01, -6.2589e-03,  5.4819e-01,\n",
       "                        4.4409e-02, -7.3185e-01,  3.3049e-01, -8.0922e-02],\n",
       "                      [ 1.7246e-01, -8.2396e-01,  2.2711e-01,  2.6986e-01, -4.9817e-01,\n",
       "                       -2.9800e-01,  1.4788e-01,  2.6129e-01,  4.0123e-01,  8.8348e-02,\n",
       "                       -5.2504e-02, -4.2764e-01,  3.9848e-01,  8.8276e-02, -8.0151e-01,\n",
       "                        3.9482e-01, -8.8953e-02,  6.4508e-02, -6.6750e-01, -2.6136e-01,\n",
       "                        2.3552e-01, -1.2795e-01, -4.6433e-01,  1.6633e-01, -9.8085e-01,\n",
       "                       -6.8846e-01, -2.9411e-01,  2.7751e-01,  5.5618e-02,  1.1645e-01,\n",
       "                        5.2586e-01, -5.5380e-03, -8.8311e-01,  7.6901e-02,  4.4206e-01,\n",
       "                        2.4667e-01,  1.0382e-01,  6.7248e-02,  9.5726e-02,  1.9349e-01,\n",
       "                        1.1129e-01, -1.3626e-01,  1.5719e-01, -5.5234e-01, -5.2332e-01,\n",
       "                        2.4220e-01, -4.1176e-01, -1.2597e-01,  2.0997e-01,  2.1665e-01,\n",
       "                        4.1073e-01,  3.7995e-01, -4.9443e-01,  1.1423e-01,  3.1720e-01,\n",
       "                        1.9590e-01, -5.6685e-01,  1.1079e-01, -7.2276e-01, -4.2463e-01,\n",
       "                       -1.0537e+00, -6.0029e-01, -5.5354e-02,  9.0775e-02],\n",
       "                      [ 2.7249e-01,  2.5989e-01, -3.4039e-01,  6.5869e-01,  8.6579e-01,\n",
       "                        2.2543e-01, -3.9133e-02,  4.2607e-01,  8.6881e-02, -1.7889e-01,\n",
       "                        6.0688e-02,  2.5121e-03,  1.1644e-01, -7.9442e-01,  1.7138e-01,\n",
       "                       -1.1430e-01,  2.4410e-01, -4.9551e-01,  2.6144e-01,  6.1743e-02,\n",
       "                       -2.7039e-01,  1.3966e-01,  1.8226e-02, -8.3690e-01,  6.2073e-02,\n",
       "                        9.4509e-02, -6.3994e-02, -5.9445e-01, -2.5096e-01,  4.3309e-01,\n",
       "                       -3.9916e-02, -2.9109e-01, -8.5893e-01,  1.6161e-01,  9.9746e-02,\n",
       "                        9.1035e-01, -4.1569e-01, -4.2971e-01,  3.4887e-01, -7.4436e-01,\n",
       "                       -5.6315e-01,  2.0350e-01, -5.4705e-01, -4.9896e-01,  2.0674e-01,\n",
       "                       -3.1200e-02,  1.4431e-01, -1.9753e-02, -6.6709e-01,  1.5714e-02,\n",
       "                       -6.0364e-03, -4.3106e-01,  2.7253e-01, -1.9770e-01,  2.5931e-01,\n",
       "                       -5.1982e-01,  5.2046e-01,  3.0491e-01,  3.3079e-01, -3.4369e-01,\n",
       "                        1.1342e-01,  4.9578e-01,  3.7019e-02, -4.4089e-01],\n",
       "                      [ 2.6471e-02, -3.6178e-01,  3.1467e-01, -8.2892e-01, -7.0239e-01,\n",
       "                        2.7714e-01,  2.1553e-01,  1.2978e-01,  1.3868e-01, -4.4371e-02,\n",
       "                       -6.4553e-02,  1.2587e-01, -1.4991e-01, -3.0353e-01,  3.2042e-01,\n",
       "                        2.9982e-01, -8.1014e-01, -3.7366e-03, -3.9659e-01, -9.4609e-01,\n",
       "                        1.3031e-01, -5.9080e-02, -3.6981e-02,  2.7322e-01,  6.8711e-02,\n",
       "                        1.9280e-02, -6.9897e-02, -4.2501e-01,  3.1912e-01, -2.0157e-01,\n",
       "                       -7.6735e-01,  1.3018e-01,  5.3122e-02, -2.7598e-01, -3.8829e-01,\n",
       "                       -5.8219e-01,  3.1829e-01, -4.9958e-01, -5.1916e-01,  1.6281e-01,\n",
       "                        2.5690e-01, -8.0108e-01,  2.1280e-01, -4.8952e-01, -7.6338e-02,\n",
       "                        2.2053e-01,  1.4870e-01, -3.7708e-01,  1.4765e-01,  2.4539e-01,\n",
       "                        1.4139e-01,  1.7098e-01, -3.3591e-01, -8.0370e-02, -5.0136e-01,\n",
       "                       -4.5631e-03, -3.4609e-01,  4.7570e-01,  1.9522e-01, -1.2465e+00,\n",
       "                        1.5001e-01, -2.9432e-01, -3.0237e-01,  1.9340e-01],\n",
       "                      [-9.6970e-01,  2.7674e-01,  2.3893e-01, -8.3470e-01, -1.1961e+00,\n",
       "                        7.0292e-02,  6.2417e-01, -4.0491e-01, -4.8934e-02,  1.6434e-01,\n",
       "                        1.5266e-01, -2.5027e-01,  2.8991e-01,  4.6108e-01, -8.8506e-02,\n",
       "                       -1.3477e-01,  3.6035e-01, -7.7622e-01,  1.3950e-01,  3.0911e-01,\n",
       "                        2.7728e-01,  2.5287e-01, -2.7724e-02, -5.1452e-02,  8.4186e-02,\n",
       "                       -1.0627e-01,  3.4708e-01,  1.8657e-02,  1.9737e-01,  5.1078e-02,\n",
       "                       -5.8793e-01, -1.4398e-01,  2.9046e-01,  1.6887e-01, -3.7126e-01,\n",
       "                       -6.0546e-01,  1.3396e-01, -3.8299e-01,  7.6774e-02, -3.1518e-01,\n",
       "                       -2.6912e-01,  3.0680e-01, -2.2953e-01, -4.8390e-01, -7.0927e-03,\n",
       "                       -1.0242e-01, -7.2282e-02,  1.9386e-01, -3.9486e-01, -1.0894e+00,\n",
       "                        2.4580e-01, -1.1102e-01,  3.4975e-01, -3.7167e-01, -3.0021e-01,\n",
       "                       -5.9844e-01, -4.6758e-01, -9.0813e-01,  1.5999e-01,  6.6300e-01,\n",
       "                        1.3439e-01, -2.6271e-01,  2.2109e-01,  1.9114e-01]], device='cuda:0')),\n",
       "             ('fc2.bias',\n",
       "              tensor([-0.3267,  0.1366,  0.0328, -0.1290, -0.1362,  0.2662, -0.1972,  0.1380,\n",
       "                      -0.0759, -0.0411], device='cuda:0'))])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看神经网络参数和值的键值对\n",
    "best_model.state_dict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "2c883fbb",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [],
   "source": [
    "# 将存储参数和值的键值对的有序字典转换为哈希表\n",
    "name_value_dict=dict(best_model.state_dict())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "8c9aa96c",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([64, 784])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看第一个全连接层权重张量的维度\n",
    "name_value_dict['fc1.weight'].shape "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "650399fb",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "50176"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 获得第一个全连接层权重张量的元素数量\n",
    "name_value_dict['fc1.weight'].numel()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7610ec15",
   "metadata": {
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "source": [
    "# 将模型参数提取为C/C++头文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "8d5ac2f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "for key in name_value_dict:\n",
    "    length=name_value_dict[key].numel() # 获得每一个张量的元素数量作为数组大小\n",
    "    flattened_tensor=name_value_dict[key].flatten().tolist() #将高维张量类型数据展平为一维列表数据\n",
    "    key=key.replace(\".\",\"_\")\n",
    "    with open(key+\".h\",\"w\") as fw:\n",
    "        fw.write(\"double {}[{}] = {{{}}};\\n\".format(key,length,str(flattened_tensor).strip(\"[]\")))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "93a316bd",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0.0178,  0.0287, -0.0187,  ...,  0.0194, -0.0052, -0.0057],\n",
       "        [-0.0235, -0.0194,  0.0319,  ...,  0.0261,  0.0152, -0.0233],\n",
       "        [-0.0305,  0.0227, -0.0342,  ...,  0.0221, -0.0182,  0.0337],\n",
       "        ...,\n",
       "        [ 0.0119,  0.0035, -0.0068,  ...,  0.0071,  0.0264,  0.0188],\n",
       "        [ 0.0082, -0.0258, -0.0171,  ...,  0.0027,  0.0207, -0.0112],\n",
       "        [-0.0096,  0.0233,  0.0208,  ...,  0.0128,  0.0138, -0.0294]],\n",
       "       device='cuda:0')"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "name_value_dict['fc1.weight']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b50de4f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.11"
  },
  "toc-autonumbering": true,
  "toc-showmarkdowntxt": false,
  "toc-showtags": false
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
